package edu.ubbcs.rs.backend.repository.jpa;

import edu.ubbcs.rs.backend.model.AbstractModel;
import edu.ubbcs.rs.backend.repository.BaseRepository;
import edu.ubbcs.rs.backend.repository.RepositoryException;
import java.io.Serializable;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceException;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Root;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public abstract class AbstractJPABaseRepository<T extends AbstractModel, I>
        implements
            BaseRepository<T, I>,
            Serializable {
    
    private static final Logger LOG                = LoggerFactory.getLogger (AbstractJPABaseRepository.class);
    private static final String GET_ALL_MESSAGE    = "Get All Data Failed";
    private static final String GET_BY_ID_MESSAGE  = "Get Data By Id Failed";
    private static final String INSERT_MESSAGE     = "Insert Data Failed";
    private static final String UPDATE_MESSAGE     = "Update Data Failed";
    private static final String DELETE_MESSAGE     = "Delete Data Failed";
    private static final String DELETE_ALL_MESSAGE = "Delete All Data Failed";
    private static final String GET_COUNT_MESSAGE  = "Get Count for Data Failed";
    private final Class<T>      clazz;
    @PersistenceContext (unitName = "reservation_system_unit")
    private EntityManager       manager;
    
    public AbstractJPABaseRepository (Class<T> clazz) {
        this.clazz = clazz;
    }
    
    protected EntityManager getEntityManager () {
        return manager;
    }
    
    @Override
    public List<T> getAll () {
        try {
            CriteriaBuilder cb = manager.getCriteriaBuilder ();
            CriteriaQuery<T> criteriaQuery = cb.createQuery (clazz);
            Root<T> rootEntry = criteriaQuery.from (clazz);
            CriteriaQuery<T> all = criteriaQuery.select (rootEntry);
            TypedQuery<T> query = manager.createQuery (all);
            return query.getResultList ();
        } catch (final IllegalArgumentException | PersistenceException ex) {
            LOG.error (GET_ALL_MESSAGE, ex);
            throw new RepositoryException (GET_ALL_MESSAGE, ex);
        }
        
    }
    
    @Override
    public T getById (I id) {
        try {
            return manager.find (clazz, id);
        } catch (final IllegalArgumentException | PersistenceException ex) {
            LOG.error (GET_BY_ID_MESSAGE, ex);
            throw new RepositoryException (GET_BY_ID_MESSAGE, ex);
        }
    }
    
    @Override
    public void insert (T item) {
        try {
            manager.persist (item);
        } catch (final IllegalArgumentException | PersistenceException ex) {
            LOG.error (INSERT_MESSAGE, ex);
            throw new RepositoryException (INSERT_MESSAGE, ex);
        }
    }
    
    @Override
    public void update (T item) {
        try {
            manager.merge (item);
        } catch (final IllegalArgumentException | PersistenceException ex) {
            LOG.error (UPDATE_MESSAGE, ex);
            throw new RepositoryException (UPDATE_MESSAGE, ex);
            
        }
    }
    
    @Override
    public void delete (T item) {
        try {
            T toRemove = manager.merge (item);
            manager.remove (toRemove);
        } catch (final IllegalArgumentException | PersistenceException ex) {
            LOG.error (DELETE_MESSAGE, ex);
            throw new RepositoryException (DELETE_MESSAGE, ex);
        }
    }
    
    @Override
    public void deleteAll () {
        try {
            String sql = "DELETE FROM " + clazz.getSimpleName ();
            manager.createQuery (sql).executeUpdate ();
        } catch (final IllegalArgumentException | PersistenceException ex) {
            LOG.error (DELETE_ALL_MESSAGE, ex);
            throw new RepositoryException (DELETE_ALL_MESSAGE, ex);
        }
    }
    
    @Override
    public Long getCount () {
        try {
            String sql = "SELECT COUNT(s) FROM " + clazz.getSimpleName () + " s";
            Query query = manager.createQuery (sql);
            return (Long) query.getSingleResult ();
        } catch (final IllegalArgumentException | PersistenceException ex) {
            LOG.error (GET_COUNT_MESSAGE, ex);
            throw new RepositoryException (GET_COUNT_MESSAGE, ex);
        }
    }
}
